package com.ecarx.library_base.scoket;

import android.util.Log;

import com.ecarx.library_base.scoket.callback.ListenerQueue;
import com.ecarx.library_base.scoket.callback.MsgCallback;
import com.ecarx.library_base.scoket.callback.Packetlistener;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.util.CharsetUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author WL on 2023/04/18
 */
public class SocketManager {
    private static final String TAG = "SocketManager";
    // 为每个域的服务，缓存服务SocketThread
    private final Map<ServerOS, SocketThread> mCacheServer = Collections.synchronizedMap(new HashMap<>());

    // 为每个域的服务，缓存对应的回调监听队列
    private final Map<ServerOS, ListenerQueue> mCacheListener = Collections.synchronizedMap(new HashMap<>());

    // 为每个域的服务，缓存每个服务的全局监听服务
    private final Map<ServerOS, List<MsgCallback>> mMsgCallbackMap = Collections.synchronizedMap(new HashMap<>());

    // 通信中的 seq 自动生成器
    private static final Singleton<SocketManager> gSocketManager = new Singleton<SocketManager>() {
        @Override
        protected SocketManager create() {
            return new SocketManager();
        }
    };

    public static SocketManager get() {
        return gSocketManager.get();
    }

    private SocketManager() {

    }

    public boolean registerCallback(ServerOS serverOS, MsgCallback callback) {
        if (callback == null || serverOS == null) {
            return Boolean.FALSE;
        }
        List<MsgCallback> list;
        if (mMsgCallbackMap.containsKey(serverOS)) {
            list = mMsgCallbackMap.get(serverOS);
        } else {
            list = new ArrayList<>();
            mMsgCallbackMap.put(serverOS, list);
        }

        if (list == null) {
            return Boolean.FALSE;
        }

        if (!list.contains(callback)) {
            list.add(callback);
        }
        return Boolean.TRUE;
    }

    public boolean unRegisterCallback(ServerOS serverOS, MsgCallback callback) {
        if (serverOS == null || callback == null) {
            return Boolean.FALSE;
        }
        List<MsgCallback> list = new ArrayList<>();
        if (mMsgCallbackMap.containsKey(serverOS)) {
            list = mMsgCallbackMap.get(serverOS);
        }

        if (list == null || list.isEmpty()) {
            return Boolean.FALSE;
        }

        return list.remove(callback);
    }

    /**
     * 连接对应 OS 服务
     *
     * @param serverOS OS域的服务配置信息
     */
    public void connectServer(ServerOS serverOS) {
        if (mCacheServer.containsKey(serverOS)) {
            SocketThread socketThread = mCacheServer.get(serverOS);
            ListenerQueue listenerQueue = mCacheListener.get(serverOS);
            mCacheServer.remove(serverOS);
            mCacheListener.remove(serverOS);
            if (socketThread != null) {
                socketThread.close();
            }
            if (listenerQueue != null) {
                listenerQueue.onDestroy();
            }


        }
        MsgServerHandler serverHandler = new MsgServerHandler(serverOS);
        SocketThread socketThread = new SocketThread(serverOS.getHost(), serverOS.getPort(), serverHandler);
        socketThread.start();
        mCacheServer.put(serverOS, socketThread);
    }

    /**
     * 断开与对应服务的连接
     *
     * @param serverOS OS域的服务配置信息
     */
    public void disConnectServer(ServerOS serverOS) {
        if (!mCacheServer.containsKey(serverOS)) {
            return;
        }

        SocketThread socketThread = mCacheServer.get(serverOS);
        if (socketThread == null) {
            return;
        }
        mCacheServer.remove(serverOS);
        socketThread.close();

        ListenerQueue listenerQueue = mCacheListener.get(serverOS);
        mCacheListener.remove(serverOS);
        if (listenerQueue != null) {
            listenerQueue.onDestroy();
        }


    }

    /**
     * 判断对应的 OS 服务是否处于连接状态
     *
     * @param serverOS OS域的服务配置信息
     * @return true - 服务器已连接
     * false - 服务器未连接
     */
    public boolean isConnected(ServerOS serverOS) {
        if (!mCacheServer.containsKey(serverOS)) {
            return Boolean.FALSE;
        }

        SocketThread socketThread = mCacheServer.get(serverOS);
        if (socketThread == null) {
            return Boolean.FALSE;
        }

        return socketThread.isConnected();
    }

    public void onMsgServerConnected(ServerOS serverOS) {
        if (!mCacheServer.containsKey(serverOS)) {
            return;
        }
        ListenerQueue listenerQueue = new ListenerQueue();
        listenerQueue.onStart();
        mCacheListener.put(serverOS, listenerQueue);

        List<MsgCallback> callbackList = mMsgCallbackMap.get(serverOS);
        if (callbackList == null || callbackList.isEmpty()) {
            return;
        }
        for (MsgCallback callback : callbackList) {
            callback.onConnected();
        }
    }

    public void onMsgServerDisconn(ServerOS serverOS) {
        if (!mCacheServer.containsKey(serverOS)) {
            return;
        }

        List<MsgCallback> callbackList = mMsgCallbackMap.get(serverOS);
        if (callbackList == null || callbackList.isEmpty()) {
            return;
        }
        for (MsgCallback callback : callbackList) {
            callback.onDisConnected();
        }
    }

    public void packetDispatch(ServerOS serverOS, ChannelBuffer channelBuffer) {
        String packetSrc = channelBuffer.toString(CharsetUtil.UTF_8);
        JsonParser parser = new JsonParser();
        JsonElement element;
        try {
            element = parser.parse(packetSrc);
        } catch (JsonSyntaxException jse) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据格式异常 JsonSyntaxException ：" + packetSrc));
            jse.printStackTrace();
            return;
        } catch (JsonIOException jie) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据格式异常 JsonIOException：" + packetSrc));
            jie.printStackTrace();
            return;
        }

        if (!element.isJsonObject()) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据异常：" + packetSrc));
            return;
        }

        JsonObject jsonObject = element.getAsJsonObject();

        if (!jsonObject.has("msgid")) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据缺少 msgid：" + packetSrc));
            return;
        }
        int msgId = jsonObject.get("msgid").getAsInt();

        if (!jsonObject.has("seq")) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据缺少 seq：" + packetSrc));
            return;
        }
        long seq = jsonObject.get("seq").getAsLong();
        // 裁剪掉上层应用不关心
        ListenerQueue listenerQueue = mCacheListener.get(serverOS);

        //
        if (listenerQueue == null) {
            // 异步接收
            if (!mMsgCallbackMap.containsKey(serverOS)) {
                return;
            }

            List<MsgCallback> callbackList = mMsgCallbackMap.get(serverOS);
            if (callbackList == null || callbackList.isEmpty()) {
                return;
            }
            for (MsgCallback callback : callbackList) {
                callback.onReceiver(msgId, jsonObject.toString());
            }
            return;
        }
        Packetlistener packetlistener = listenerQueue.pop(seq);
        if (packetlistener == null) {
            // 异步接收
            if (!mMsgCallbackMap.containsKey(serverOS)) {
                return;
            }
            List<MsgCallback> callbackList = mMsgCallbackMap.get(serverOS);
            if (callbackList == null || callbackList.isEmpty()) {
                return;
            }
            for (MsgCallback callback : callbackList) {
                callback.onReceiver(msgId, jsonObject.toString());
            }
            return;
        }
        // 对应的 seq 存在于 ListenerQueue，则代表是对应 seq 的响应数据，必须携带 code
        // 其中 code = 0 代表成功，否则失败
        int code = jsonObject.get("code").getAsInt();
        if (!jsonObject.has("code")) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据缺少 code：" + packetSrc));
            return;
        }
        if (code == 0) {
            packetlistener.onSuccess(msgId, jsonObject.toString());
        } else {
            if (!jsonObject.has("errormsg")) {
                packetlistener.onFail(code, "内部错误");
            } else {
                String errormsg = jsonObject.get("errormsg").getAsString();
                packetlistener.onFail(code, errormsg);
            }

        }
    }

    /**
     * @param serverOS OS域的服务配置信息
     */
    public void onConnectMsgServerFail(ServerOS serverOS) {
        if (!mCacheServer.containsKey(serverOS)) {
            return;
        }

        List<MsgCallback> callbackList = mMsgCallbackMap.get(serverOS);
        if (callbackList == null || callbackList.isEmpty()) {
            return;
        }
        for (MsgCallback callback : callbackList) {
            callback.onConnectFail();
        }

    }

    public boolean sendRequest(ServerOS serverOS, String jsonMsg) {
        return sendRequest(serverOS, jsonMsg, null);
    }

    public boolean sendRequest(ServerOS serverOS, String jsonMsg, Packetlistener packetlistener) {
        if (!mCacheServer.containsKey(serverOS)) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "服务器不存在 ：" + jsonMsg));
            return Boolean.FALSE;
        }

        SocketThread socketThread = mCacheServer.get(serverOS);
        if (socketThread == null) {
            Log.e(TAG, "SocketThread is NULL");
            return Boolean.FALSE;
        }

        JsonParser parser = new JsonParser();
        JsonElement element;
        try {
            element = parser.parse(jsonMsg);
        } catch (JsonSyntaxException jse) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据格式异常 JsonSyntaxException ：" + jsonMsg));
            jse.printStackTrace();
            return Boolean.FALSE;
        } catch (JsonIOException jie) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据格式异常 JsonIOException：" + jsonMsg));
            jie.printStackTrace();
            return Boolean.FALSE;
        }

        if (!element.isJsonObject()) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据异常：" + jsonMsg));
            return Boolean.FALSE;
        }

        JsonObject jsonObject = element.getAsJsonObject();

        if (!jsonObject.has("msgid")) {
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(), "数据缺少 msgid：" + jsonMsg));
            return Boolean.FALSE;
        }
        long seqNo = System.currentTimeMillis();
        if (jsonObject.has("seq")) {
            seqNo = jsonObject.get("seq").getAsLong();
        } else {
            jsonObject.addProperty("seq", seqNo);
        }

        ListenerQueue listenerQueue = mCacheListener.get(serverOS);
        try {
            if (packetlistener != null && listenerQueue != null)
                listenerQueue.push(seqNo, packetlistener);
            boolean sendRes = socketThread.sendRequest(jsonObject.toString());
            return sendRes;
        } catch (Exception e) {
            if (packetlistener != null) {
                packetlistener.onFail(1001, "发送失败");
            }
            if (listenerQueue != null)
                listenerQueue.pop(seqNo);
            Log.e(TAG, String.format("%s:%d -- %s", serverOS.getHost(),
                    serverOS.getPort(),
                    "#sendRequest#channel is close!"));
        }
        return Boolean.FALSE;
    }


    public enum ServerOS {
//        Hypervisor_OS_Upgrade("127.0.0.1", 1890),
        Hypervisor_OS(SocketConfig.DOM0.HOST, SocketConfig.DOM0.LOG_PORT),
        Cluster_OS(SocketConfig.CLUSTER.HOST, SocketConfig.CLUSTER.LOG_PORT),
        Hypervisor_Update_OS(SocketConfig.DOM0.HOST, SocketConfig.DOM0.PORT),
        Game_OS(SocketConfig.GAME.HOST, SocketConfig.GAME.LOG_PORT);


        private final String mHost;
        private final int mPort;

        ServerOS(String ip, int port) {
            this.mHost = ip;
            this.mPort = port;
        }

        public String getHost() {
            return this.mHost;
        }

        public int getPort() {
            return this.mPort;
        }


    }
}
